/**
 * Project of Vehicle Routing Problem with Time Windows implements 
 * some of well-known algorithms for solving VRPTW and presents new 
 * one.
 *
 * @author Michal Drobny  
 */

package heuristic;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import logic.algorithm.Algorithm;
import logic.algorithm.AlgorithmResult;
import logic.algorithm.AlgorithmState;
import logic.car.VehicleProvider;
import logic.destination.Destination;
import logic.evaluator.Evaluator;
import logic.restriction.enumeration.ChangeCycleVariant;
import logic.restrictionAdapter.RestrictionAdapter;
import logic.route.Cycle;
import logic.route.Route;

/**
 * Represents the abstract logic class for inserting methods for solving VRP
 * problem.
 * 
 * @author Michal Drobny
 * @date 5.11.2014
 */
public abstract class AbstractInsertingMethod implements Algorithm {

	@Override
	public AlgorithmResult compute(Route route, Destination start,
			Evaluator evaluator, RestrictionAdapter restrictionAdapter, VehicleProvider vehicleProvider) {

		if (route == null || start == null || evaluator == null
				|| restrictionAdapter == null || vehicleProvider == null)
			return new AlgorithmResult(AlgorithmState.BAD_PARAMETERS, null,
					route);

		if (!route.getDestinations().contains(start))
			return new AlgorithmResult(AlgorithmState.BAD_START_DESTINATION,
					null, route);

		Set<Destination> unusedDestinations = new HashSet<Destination>(
				route.getDestinations());
		unusedDestinations.remove(start);

		List<Cycle> cycles = new ArrayList<Cycle>();
		Cycle cycle = null;
		while (!unusedDestinations.isEmpty()) {
			cycle = new Cycle();
			cycle.addToEnd(start);
			Cycle newCycle;
			while ((newCycle = addNextDestination(cycle, unusedDestinations, evaluator,
					restrictionAdapter)) != null) {
				cycle = newCycle;
			}
			
			if (cycle.size() < 2 || !restrictionAdapter.canNewCycle(ChangeCycleVariant.VEHICLE_PROVIDER, vehicleProvider))
				return new AlgorithmResult(AlgorithmState.NO_SOLUTION, null,
						route);
			else {
				cycles.add(cycle);
				restrictionAdapter.newCycle(ChangeCycleVariant.VEHICLE_PROVIDER, vehicleProvider);
			}
		}
		
		if (cycles.size() > 1)
			return new AlgorithmResult(AlgorithmState.MORE_CYCLE_SOLUTION,
					cycles.toArray(new Cycle[cycles.size()]), route);
		else
			return new AlgorithmResult(AlgorithmState.ONE_CYCLE_SOLUTION,
					cycles.toArray(new Cycle[cycles.size()]), route);
	}

	/**
	 * This method is different for every inserting method. Main logic - updates
	 * the given cycle in order to add new unused destination, removes this
	 * destination from unused destinations set and return true. The method has
	 * to check, if the updated cycle pass restriction adapter. If no suitable
	 * destination is found, return false.
	 * 
	 * @param cycle
	 *            The cycle
	 * @param unusedDestinations
	 *            The unused destinations
	 * @param evaluator
	 *            The evaluator
	 * @param restrictionAdapter
	 *            The restriction adapter
	 * @return whether the cycle was updated
	 */
	protected abstract Cycle addNextDestination(Cycle cycle,
			Set<Destination> unusedDestinations, Evaluator evaluator,
			RestrictionAdapter restrictionAdapter);
}